/**
 * Copyright (c) 2022 Beijing Jiaotong University
 * PhotLab is licensed under [Open Source License].
 * You can use this software according to the terms and conditions of the [Open Source License].
 * You may obtain a copy of [Open Source License] at: [https://open.source.license/]
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *
 * See the [Open Source License] for more details.
 *
 * Author: Zhenjie Wei
 * Created: Sep. 7, 2023
 * Supported by: National Key Research and Development Program of China
 */

import { Point, Rect } from '@/common/logic/common/types2D'
import IRenderable from '@/common/logic/mixins/renderable'
import LogicCore from '@/common/logic/core'
import IObjectArena from '@/common/logic/arena/arena'
import { Movable } from '@/common/logic/mixins/movable'
import RenderNode from '../models/node'

export default class Component extends Movable implements IRenderable {
    private _moving: boolean = false
    private _resizing: boolean = false
    private _arena: IObjectArena<Rect> | null = null
    private _node: RenderNode
    private _icon: HTMLImageElement = new Image()
    private _iconReady: boolean = false

    constructor(node: RenderNode) {
        super(node.id, 0, node.rect)
        this._node = node
        try {
            this._icon.src = require(`@/assets/icons/components/${node.icon}`)
        } catch (e) {
            this._icon.src = require(`@/assets/icons/unknown.svg`)
        }
        this._icon.onload = () => {
            this._iconReady = true
            this.core?.renderAll()
        }
    }

    public get node() {
        return this._node
    }

    public onRegistered(core: LogicCore): void {
        super.onRegistered(core)
        this._arena = core.logicArena
        core.on('movobj.logic.finish', this.onMoveFinished.bind(this))
    }

    public renderAt(ctx: CanvasRenderingContext2D, rect: Rect): Rect {
        // draw icon
        if (this._iconReady) {
            ctx.drawImage(this._icon, ...rect.ltwh)
        } else {
            // render a placeholder (a white rect with a cross inside)
            ctx.fillStyle = '#ffffff'
            ctx.fillRect(...rect.ltwh)
            ctx.strokeStyle = '#000000'
            ctx.lineWidth = 1
            ctx.beginPath()
            ctx.moveTo(rect.left, rect.top)
            ctx.lineTo(rect.left + rect.width, rect.top + rect.height)
            ctx.moveTo(rect.left + rect.width, rect.top)
            ctx.lineTo(rect.left, rect.top + rect.height)
            ctx.stroke()
        }
        return rect
    }

    public renderOn(ctx: CanvasRenderingContext2D) {
        const renderRect = this.core!.crd2posRect(this.rect).float()
        const rect = this.renderAt(ctx, renderRect)
        // if this component is moving or resizing, render a mask
        if (this._moving || this._resizing) {
            ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'
            ctx.fillRect(...rect.ltwh)
        }
    }

    public onMoveBegin(): void {
        this._moving = true
    }

    public onMoveEnd(): void {
        const success = this._arena!.setObject(this.id, this.target.clone())
        if (success) {
            this.rect = this.target
            this.core?.fire('comp.move', this.id, this.target.pos)
        }
    }

    public onMoveFinished(): void {
        this._moving = false
        this.target = this.rect.clone()
        this.core!.renderAll()
    }

    public onMoving(oldPos: Point, newPos: Point): boolean {
        const occupied = this._arena!.rectOccupied(this.target, this.id, true)
        // this.core?.fire('comp.move', this.id, this.target.pos)
        return occupied === null
    }
}
